#!/usr/bin/env python

import re
import os
import sys
import time
import socket
import sqlite3
import threading
import xmlrpclib
from SimpleXMLRPCServer import SimpleXMLRPCServer
import logging
import logging.handlers
from optparse import OptionParser

import autobbotd
from autobbotd.daemon import Daemon
from autobbotd.bbconfig import *

BUILDBOTDIR = os.path.dirname(autobbotd.__file__)

import buildbotweb
os.environ['DJANGO_SETTINGS_MODULE'] = 'buildbotweb.settings'
from buildbotweb.buildbot.models import ( 
    TestSuites,  
)
from buildbotweb.buildbot import models

class CTBuildBot(Daemon):
    """
    CTBuildBot

    Buildbot daemon.  Runs in the background listening on BOTPORT for paths to build.  Configuration
    is held in the .buildbotdb
    """

    buildPaths = []
    dostop = False

    def __init__(self, *args, **kwds):
        self._con = sqlite3.Connection(DBPATH)
        self._cur = self._con.cursor()
        self._cur.execute("""
            SELECT logfile, releasedir, svnuser
            FROM buildbot_btester;
        """)
        data = self._cur.fetchone()
        if not data:
            self.logfile = "/tmp/buildbot.log"
            self.releasedir = "/home/build/release"
            self.svnuser = "build"
        else:
            self.logfile = data[0]
            self.releasedir = data[1]
            self.svnuser = data[2]

        self.log = logging.getLogger("autobbotd")
        self.log.setLevel(logging.DEBUG)
        
        loghandler = logging.handlers.RotatingFileHandler(
                self.logfile,
                maxBytes=1048576,
                backupCount=5
        )

        formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
        loghandler.setFormatter(formatter)
        self.log.addHandler(loghandler)
        self.log.info("Initializing CTBuildBot")

        Daemon.__init__(self, *args, **kwds)

    def safeStop(self):
        """safeStop - Attempt to safely schedule a stop."""
        self.dostop = True
        self.log.info("Scheduling shutdown...")
        self.shuttingDown = True
        while self.shuttingDown:
            time.sleep(5)
        return 0

    def newfunc(self):
        self.log.info("NEW FUNCTION2!")
        return "newer!!"
   
    def localXMLRPC(self, port):
        """localXMLRPC - XMLRPC server for locally available functions."""
        self.log.info("localXMLRPC")
        try:
            server = SimpleXMLRPCServer(("localhost", port))
            self.log.info("XMLRPC Server listening on (%s, %s)" % ("localhost", port))
            server.register_introspection_functions()
            server.register_function(self.safeStop)
            server.serve_forever()
        except Exception, err:
            self.log.exception(err)

    def serveXMLRPC(self, host, port):
        """serveXMLRPC - XMLRPC server for publically available functions."""
        self.log.info("serveXMLRPC")
        try:
            server = SimpleXMLRPCServer((host, port))
            self.log.info("XMLRPC Server listening on (%s, %s)" % (host, port))
            server.register_introspection_functions()
            server.register_function(self.queuePaths)
            server.register_function(self.newfunc)
            server.serve_forever()
        except Exception, err:
            self.log.exception(err)

    def run(self):
        """run - Kicked off from daemon.start.  This is our worker process."""
        self.log.info("Run buildbot.")

        # Kickoff a thread for exposing some items via XMLRPC
        self.log.info("Start XMLRPC Listeners")
        try:
            t1 = threading.Thread(target=self.serveXMLRPC, args=(BOTHOST, BOTPORT))
            t2 = threading.Thread(target=self.localXMLRPC, args=(LOCALPORT,))
            self.log.info("Threads created")
            t1.start()
            t2.start()
        except Exception, err:
            self.log.error("Trouble setting up XMLRPC Thread!")
            self.log.exception(err)
        self.log.info("Thread started")

        # Setup the models log
        models.logger = self.log
        self.log.debug("Setup log for models.")

        ret = 0
        while not ret:
            try:
                self._run()
            except Exception, err:
                self.log.error("ERROR DETECTED")
                self.log.exception(err)
                self.log.info("Continue testing")
            time.sleep(10)
        
        self.log.info("Join XMLRPC Server thread.")
        t1.join(10)
        t2.join(10)

    def queuePaths(self, paths):
        """queuePaths <paths> - Queue up a list of paths to test."""
        self.log.info("Queueing Paths: %s" % paths)
        self.buildPaths.extend(paths)
        # XMLRPC expects a return code
        return 0

    def _run(self):
        """_run - Performs the work of building and testing the product."""

        # Check to see if we require a restart
        if self.dostop:
            self.log.info("Stopping.")
            self.shuttingDown = False
            return 1

        if not self.buildPaths:
            return 0

        # Get data and reset the buildPaths
        srclist = set(map(lambda x: x.strip(), self.buildPaths))
        self.buildPaths = []
        self.log.debug("SRCLIST: %s" % srclist)

        self._cur.execute("""
            SELECT 
                buildbot_testsuites.id as testsuite_id, 
                srcdir,
                testsuite, 
                testpath 
            FROM buildbot_testsuites 
            INNER JOIN buildbot_testpaths 
            ON buildbot_testsuites.testpath_id = buildbot_testpaths.id;
        """)
        data = self._cur.fetchall()
        for row in data:
            # Step through each test suite and look for a match
            suiterun = False
            testpath = row[3]
            testname = row[2]
            srcdir = row[1]
            suiteid = row[0]
            path_re = re.compile(testpath)
            for src in srclist:
                # Check if the path triggers the testsuite
                #self.log.debug("SRC: %s" % src)
                rematch = path_re.match(src)
                if rematch and not suiterun:
                    srcpath = rematch.groupdict().get('srcpath', srcdir)
                    self.log.debug("SRCPATH: %s" % srcpath)
                    # We don't want to run the same suite multiple times
                    suiterun = True
                    self.log.info("About to run suite %s" % testname)
                   
                    ts = TestSuites.objects.get(id=suiteid)
                    try:
                        ts.run(srcpath, self.log) 
                    except Exception, err:
                        self.log.error("ERROR DETECTED")
                        self.log.exception(err)
                    self.log.info("Done running suite %s" % testname)


if __name__ == "__main__":
    parser = OptionParser()
    opts, args = parser.parse_args()

    daemon = CTBuildBot('/tmp/autobbotd.pid')
    if len(args) == 1:
        if 'start' == args[0]:
            daemon.start()
        elif 'stop' == args[0]:
            try:
                xmlrpclib.ServerProxy("http://%s:%s" % ("localhost", LOCALPORT)).safeStop()
            except socket.error:
                print "Appears to already be stopped."
            daemon.stop()
        elif 'restart' == args[0]:
            xmlrpclib.ServerProxy("http://%s:%s" % ("localhost", LOCALPORT)).safeStop()
            daemon.restart()
        elif 'forcestop' == args[0]:
            daemon.stop()
        else:
            print "Unknown command"
            sys.exit(2)
        sys.exit(0)
    else:
        print "usage: %s start|stop|forcestop|restart" % args
        sys.exit(2)

